using CfgTable;
using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using NetProtocol.Client;
using NetProtocol.POD;
using System;
using System.Collections.Generic;
using static NetProtocol.Client.CNetItem;

namespace IQIGame.Onigao.GamePlay
{
    /// <summary>
    /// 类名 : 模块 - 道具
    /// 作者 : Canyon / 龚阳辉
    /// 日期 : 2024-05-16 16:41
    /// 功能 : 
    /// </summary>
    public class ItemModule : Singleton<ItemModule>, CNetItem
    {
        private ItemModule() { }

        private ItemModuleData itemModuleData => GameDataCenter.PlayerData.item;

        public void InitModule(PlayerPOD playerPOD)
        {
            ItemModuleData itemModuleData = new ItemModuleData(playerPOD.basicItems, playerPOD.itemDebts, playerPOD.itemCdInfo, playerPOD.exchangeRecords);
            GameDataCenter.PlayerData.item = itemModuleData;
            GameDataCenter.PlayerData.portableItemId = playerPOD.portableItemId;

            var items = playerPOD.items;
            int cnt = items.Count;
            for (int i = 0; i < cnt; i++)
            {
                ItemPOD item = items[i];
                this.ChangeItem(item);
                PODPool<ItemPOD>.Put(item);
            }
            items.Clear();
        }

        public void ClearModule()
        {
            var data = GameDataCenter.PlayerData.item;
            GameDataCenter.PlayerData.item = null;
            data?.Clear();
        }

        #region Net层
        public static CNetItem NetCore => Instance;
        void CNetItem.SC_notifyItemChange(ISession session, List<ItemPOD> updateList)
        {
            for (int i = 0; i < updateList.Count; i++)
            {
                var item = updateList[i];
                this.ChangeItem(item);
                MsgDispatcher.Broadcast(MsgEventType.OneItemChanged, item.cid);
                PODPool<ItemPOD>.Put(item);
            }
            MsgDispatcher.Broadcast(MsgEventType.ItemChanged);
        }

        void CNetItem.SC_notifyItemRemove(ISession session, List<long> removeList)
        {
            long id = 0;
            ItemData itemData = null;
            for (int i = 0; i < removeList.Count; i++)
            {
                id = removeList[i];
                if (this.itemModuleData.TryRemoveItem(id, out itemData))
                {
                    MsgDispatcher.Broadcast(MsgEventType.OneItemChanged, itemData.cid);
                    itemData.Put2Pool();
                }
            }
            MsgDispatcher.Broadcast(MsgEventType.ItemChanged);
        }

        void CNetItem.SC_notifyBasicItemChange(ISession session, Dictionary<int, int> updateList)
        {
            foreach (var item in updateList)
            {
                if (!this.itemModuleData.TryGetItem(item.Key, out var itemData))
                {
                    itemData = ItemData.ConvertFromBasicItem(item.Key, item.Value);
                    this.itemModuleData.AddItem(itemData);
                }
                itemData.SetNum(item.Value);

                MsgDispatcher.Broadcast(MsgEventType.OneItemChanged, item.Key);
            }
            MsgDispatcher.Broadcast(MsgEventType.ItemChanged);
        }

        void CNetItem.SC_notifyItemDebts(ISession session, Dictionary<int, int> itemDebts)
        {
            this.itemModuleData.UpItemDebts(itemDebts);
        }

        void CNetItem.SC_notifyItemCdInfo(ISession session, Dictionary<int, int> itemCdInfo)
        {
            this.itemModuleData.UpItemCdInfo(itemCdInfo);
        }

        void CNetItem.SC_notifyUpdatePortableItem(ISession session, long itemId)
        {
            GameDataCenter.PlayerData.portableItemId = itemId;
            MsgDispatcher.Broadcast(MsgEventType.PortableItemSwitch, itemId);
        }

        ISession CNetItem.GetSession()
        {
            return GameServerModule.Instance.CurrSession;
        }

        public static UniTask<UseItemResultData> CS_useItem(long itemId, int itemCid, int useNum, List<int> chooses)
        {
            UseItemRequestPOD requestPOD = MakeUseItemRequestPOD(itemId, itemCid, useNum);
            if (chooses != null && chooses.Count > 0)
                requestPOD.chooses.AddRange(chooses);
            return NetCore.CS_useItem(requestPOD);
        }

        private static UseItemRequestPOD MakeUseItemRequestPOD(long itemId, int itemCid, int useNum)
        {
            UseItemRequestPOD requestPOD = new UseItemRequestPOD();
            requestPOD.itemCid = itemCid;
            requestPOD.itemId = itemId;
            requestPOD.num = useNum;
            return requestPOD;
        }

        public static UniTask<UseItemResultData> CS_useItem(long itemId, int itemCid, int useNum = 1, params int[] chooses)
        {
            UseItemRequestPOD requestPOD = MakeUseItemRequestPOD(itemId, itemCid, useNum);
            if (chooses != null && chooses.Length > 0)
                requestPOD.chooses.AddRange(chooses);
            return NetCore.CS_useItem(requestPOD);
        }
        #endregion

        protected void ChangeItem(ItemPOD item)
        {
            var cfgOne = TableCenter.item.Get(item.cid);
            if (cfgOne == null)
            {
                return;
            }
            ItemData itemCache = null;
            if (this.itemModuleData.TryGetItem(item.id, out itemCache))
            {
                itemCache.Sync(item);
            }
            else
            {
                itemCache = ItemData.ConvertFromPOD(item);
                this.itemModuleData.AddItem(itemCache);
            }
        }

        public void UpItemCdInfo(Dictionary<int, int> itemCdInfo)
        {
            this.itemModuleData.UpItemCdInfo(itemCdInfo);
        }

        public ItemData GetItemById(long id)
        {
            if (this.itemModuleData.TryGetItem(id, out var item))
                return item;
            return null;
        }

        public ItemData GetItemOneByCid(int cid)
        {
            CfgTable.CfgItem cfgItem = TableCenter.item.Get(cid);
            if (cfgItem != null)
            {
                if (cfgItem.Type == ItemType.Basic)
                {
                    return this.GetItemById(cid);
                }
                var list = itemModuleData.GetList(MakeFuncCheckIsSameCid(cid));
                if (list.Count > 0)
                    return list[0];
            }
            return null;
        }

        private System.Func<ItemData, bool> MakeFuncCheckIsSameCid(int cid)
        {
            return item =>
            {
                return item.cid == cid;
            };
        }

        public IReadOnlyList<ItemData> GetItemAllByCid(int cid)
        {
            CfgTable.CfgItem cfgItem = TableCenter.item.Get(cid);
            if (cfgItem != null)
            {
                List<ItemData> list = ListPool<ItemData>.Get();
                if (cfgItem.Type == ItemType.Basic)
                {
                    var itemData = GetItemById(cid);
                    if (itemData != null)
                    {
                        list.Add(itemData);
                    }
                }
                else
                {
                    var sameCidList = itemModuleData.GetList(MakeFuncCheckIsSameCid(cid));
                    list.AddRange(sameCidList);
                }
                IReadOnlyList<ItemData> ret = list.ToArray();
                ListPool<ItemData>.Put(list);
                return ret;
            }
            return null;
        }

        public int GetNum(int cid, Func<ItemData, bool> filter = null)
        {
            if (this.itemModuleData == null)
            {
                return 0;
            }
            if (cid == ItemConst.ItemCid_PayPoint_ForShow)
            {
                return GameDataCenter.PlayerData.payPoint;
            }
            int cnt = 0;
            CfgTable.CfgItem cfgItem = TableCenter.item.Get(cid);
            if (cfgItem == null)
            {
                return cnt;
            }
            if (cfgItem.Type == ItemType.Basic)
            {
                if (itemModuleData.TryGetItem(cid, out var item))
                {
                    if (filter == null || filter(item))
                        cnt = item.num;
                }
            }
            else
            {
                var sameCidList = itemModuleData.GetList(MakeFuncCheckIsSameCid(cid));
                foreach (var item in sameCidList)
                {
                    if (filter == null || filter(item))
                        cnt++;
                }
            }
            return cnt;
        }

        // Filter - Leach
        public List<ItemEquipmentData> LeachEquipList(List<ItemEquipmentData> orgList, EquipFilter filter, List<ItemEquipmentData> destList = null)
        {
            if (destList == null)
                destList = new(16);
            bool isDefault = filter == EquipFilter.Default;
            foreach (var data in orgList)
            {
                bool isCanAdd = isDefault || this.IsCanAddData(data, filter);
                if (isCanAdd)
                {
                    destList.Add(data);
                }
            }
            return destList;
        }

        private bool IsCanAddData(ItemEquipmentData data, EquipFilter filter)
        {
            bool isCanAdd = IsCanAddTypeState(data, filter) && IsCanAddQuality(data, filter);
            return isCanAdd && IsCanAddWearState(data, filter);
        }

        private bool IsCanAddTypeState(ItemEquipmentData data, EquipFilter filter)
        {
            return (filter.locationType == 0) || (data.cfgData.SubType == filter.locationType);
        }

        private bool IsCanAddQuality(ItemEquipmentData data, EquipFilter filter)
        {
            return (filter.quality == 0) || (data.cfgData.Quality == filter.quality);
        }

        private bool IsCanAddWearState(ItemEquipmentData data, EquipFilter filter)
        {
            if (filter.wearState <= 0)
                return true;
            bool isWear = filter.wearState == 1;
            var wearer = data.currWearer;
            if (isWear)
            {
                return wearer != null;
            }
            return wearer == null;
        }

        public void ReorderEquipList(List<ItemEquipmentData> equipItems, EquipSort sort, System.Func<ItemEquipmentData, ItemEquipmentData, int> funcOutSort = null)
        {
            int cnt = equipItems?.Count ?? 0;
            if (cnt <= 1)
                return;
            var _sortFunc = this.MakeEquipListSortFunc(sort, funcOutSort);
            equipItems.Sort(_sortFunc);
        }

        private System.Comparison<ItemEquipmentData> MakeEquipListSortFunc(EquipSort sort, System.Func<ItemEquipmentData, ItemEquipmentData, int> funcOutSort = null)
        {
            return (ItemEquipmentData a, ItemEquipmentData b) =>
            {
                if (funcOutSort != null)
                {
                    int valOutSort = funcOutSort(a, b);
                    if (valOutSort != 0)
                    {
                        return valOutSort;
                    }
                }

                long sortA = 0;
                long sortB = 0;
                UIEquipSortType eType = (UIEquipSortType)sort.sortType;
                switch (eType)
                {
                    case UIEquipSortType.Quality:
                        sortA = a.cfgData.Quality;
                        sortB = b.cfgData.Quality;
                        break;
                    case UIEquipSortType.Star:
                        sortA = a.star;
                        sortB = b.star;
                        break;
                    case UIEquipSortType.Lv:
                        sortA = a.level;
                        sortB = b.level;
                        break;
                    default:
                        break;
                }
                if (sortA == sortB)
                {
                    sortA = a.cfgData.Id;
                    sortB = b.cfgData.Id;
                    if (sortA == sortB)
                    {
                        sortA = a.id;
                        sortB = b.id;
                    }
                }
                int symbol = sort.isDesc ? -1 : 1;
                return sortA > sortB ? 1 * symbol : -1 * symbol;
            };
        }

        public int GetCanUseMaxNum(ItemData itemData)
        {
            return this.GetCanUseMaxNum(itemData.cid, itemData.id);
        }

        public int GetCanUseMaxNum(int itemCid, long itemSid = 0)
        {
            var cfg = TableCenter.item.Get(itemCid);
            if (cfg == null)
                return 0;
            var svData = itemSid > 0 ? this.GetItemById(itemSid) : null;
            int usedNum = svData?.usedNum ?? 0;
            int cntHas = this.GetNum(itemCid);
            return Math.Min(cntHas, cfg.BatchUseLimit - usedNum);
        }


        /// <summary>
        /// 相同道具id合并数量进行显示（目前只合并了ItemType为Basic的基础道具类型）
        /// </summary>
        /// <param name="list"></param>
        /// <returns></returns>
        public void ConvertListFrom(List<ItemShowPOD> list, List<ItemShowData> result)
        {
            if (list == null || list.Count == 0)
            {
                return;
            }
            var tempItemDict = new Dictionary<int, ItemShowData>();
            foreach (ItemShowPOD item in list)
            {
                var cfg = TableCenter.item.Get(item.cid);
                if (cfg == null)
                {
                    PODPool<ItemShowPOD>.Put(item);
                    continue;
                }

                if (cfg.Type != ItemType.Basic)
                {
                    result.Add(ItemShowData.ConvertFromPOD(item));
                    PODPool<ItemShowPOD>.Put(item);
                    continue;
                }

                if (!tempItemDict.ContainsKey(item.cid))
                {
                    var tempItem = ItemShowData.ConvertFromPOD(item);
                    tempItemDict.Add(tempItem.cid, tempItem);
                    result.Add(tempItem);
                }
                else
                {
                    tempItemDict[item.cid].num += item.num;
                }

                PODPool<ItemShowPOD>.Put(item);
            }
        }
    }
}
